function limit (min, max, num) {
  num = num > max ? max : num
  num = num < min ? min : num
  return num
}

function isRgb (rgb) {
  return /^rgb\s*\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*\)/gi.test(rgb)
}

function isHex (hex) {
  return /^#\s*([0-9a-fA-F]{3}){1,2}$/gi.test(hex)
}

function isHsl (hsl) {
  let isFormatCorrect = /^hsl\(\s*\d+\s*,\s*\d+\s*%,\s*\d+\s*%\)/gi.test(hsl)
  if (!isFormatCorrect) { return false }
  let nums = hsl.match(/\d+/gi)
  let isInScopeMax = +nums[0] <= 360 && +nums[1] <= 100 && + nums[2] <= 100
  let isInScopeMin = +nums[0] >= 0 && +nums[1] >= 0 && + nums[2] >= 0
  return isFormatCorrect && isInScopeMax && isInScopeMin
}

/**
 * two bit of Hex  adjuest double
 * @examle
 * fe => ff => f; 07 => 00 => 0
 */
function adjustDouble (hex) {
  let hexNum = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f']
  hex = String(hex).toLowerCase()
  if (parseInt(hex, 16) % 17 < 9) {
    return hex[0]
  }
  let idx = hexNum.indexOf(hex[0])
  idx = idx >= 14 ? 14 : idx
  return hexNum[idx + 1]
}

function simplifyHex (hex) {
  let nums = []
  hex.match(/[0-9a-fA-F]{2}/gi).forEach((num) => {
    nums.push(adjustDouble(num))
  })
  return `#${nums.join('')}`
}

exports.toHex = function () {
  let color = this._checkType('String')
  if (!isHex(color) && !isRgb(color) || (isHex(color) && color.length === 7)) { return this }

  let hex = '#'
  if (isHex(color)) {
    for (let num of color.substring(1)) { hex += num.repeat(2) }
    this._ = hex
    return this
  }
  color.toString().match(/\d+/g).map((num) => {
    return limit(0, 255, num)
  }).forEach((color) => {
    let numStr = Number(color).toString(16)
    hex += numStr.length === 1 ? '0' + numStr : numStr
  })
  this._ = hex
  return this
}

exports.to3Hex = function () {
  let color = this._checkType('String')
  if (!isRgb(color) && !isHex(color)) { return this }
  if (isRgb(color)) { color = this.toHex().val }
  this._ = simplifyHex(color)
  return this
}

exports.toRgb = function () {
  let color = this._checkType('String')
  if (!isHex(color) && !isHsl(color)) return this

  if (isHsl(color)) {
    this._ = _hslToRgb(color)
    return this
  }

  let rgb = []
  if (isHex(color) && color.length === 4) {
    rgb = [color[1], color[2], color[3]]
    rgb = rgb.map((val) => { return val.repeat(2) })
  } else if (isHex(color) && color.length === 7) {
    rgb = [color[1] + color[2], color[3] + color[4], color[5] + color[6]]
  }
  rgb = rgb.map((val) => { return parseInt(val, 16) })
  this._ = `rgb(${rgb.join()})`
  return this
}

exports.color = function () {
  let color = '#' + Math.floor(Math.random() * (2 << 23)).toString(16)
  if (color.length === 6) { color += '0' }
  this._ = color
  return this
}

function _hslToRgb(hsl) {
  let [h, s, l] = hsl.match(/\d+/gi)
  let [H, S, L] = [+h / 360, +s / 100, +l / 100]
  let [R, G, B] = [0, 0, 0]

  if (S === 0) {
    R = G = B = L // achromatic
  } else {
    let tmp2 = L < 0.5 ? L * (1 + S) : L + S - L * S
    let tmp1 = 2 * L - tmp2
    R = __hue2rgb(tmp1, tmp2, H + 1 / 3)
    G = __hue2rgb(tmp1, tmp2, H)
    B = __hue2rgb(tmp1, tmp2, H - 1 / 3)
  }
  let rgb = [R, G, B].map((val) => { return Math.round(val * 255) })
  return `rgb(${rgb})`
}

function __hue2rgb(tmp1, tmp2, t) {
  if (t < 0) t += 1
  if (t > 1) t -= 1
  if (t < 1 / 6) return tmp1 + (tmp2 - tmp1) * 6 * t
  if (t < 1 / 2) return tmp2
  if (t < 2 / 3) return tmp1 + (tmp2 - tmp1) * (2 / 3 - t) * 6
  return tmp1
}
